package com.admin.education.repositories;

import com.admin.education.config.DMLPOJOAnnotation;
import com.querydsl.core.types.EntityPath;
import com.querydsl.core.types.Expression;
import com.querydsl.jpa.HQLTemplates;
import com.querydsl.jpa.impl.JPADeleteClause;
import com.querydsl.jpa.impl.JPAQuery;
import com.querydsl.jpa.impl.JPAUpdateClause;
import org.springframework.beans.factory.annotation.Autowired;

import javax.inject.Provider;
import javax.persistence.EntityManager;
import javax.persistence.LockModeType;
import java.lang.reflect.ParameterizedType;
import java.util.List;

public abstract class AbstractRepository<T extends Identifiable> implements Repository<T, Long> {
    @Autowired
    private Provider<EntityManager> em;

    public <T> JPAQuery<T> selectFrom(EntityPath<T> entity) {
        return select(entity).from(entity);
    }

    public <T> JPAQuery<T> select(Expression<T> select) {
        return new JPAQuery<>(em.get(), HQLTemplates.DEFAULT).select(select);
    }

    public JPADeleteClause delete(EntityPath<?> entity) {
        return new JPADeleteClause(em.get(), entity, HQLTemplates.DEFAULT);
    }

    public JPAUpdateClause update(EntityPath<?> entity) {
        return new JPAUpdateClause(em.get(), entity, HQLTemplates.DEFAULT);
    }

    public void detach(Object entity) {
        em.get().detach(entity);
    }

    public <E> E find(Class<E> type, Long id) {
        return em.get().find(type, id);
    }

    public <E> E find(Class<E> type, Long id, LockModeType lockMode) {
        return em.get().find(type, id, lockMode);
    }

    @DMLPOJOAnnotation(action = "Create")
    public void persist(Object entity) {
        if (entity instanceof List) {
            List entities = (List) entity;
            for (Object o : entities) {
                em.get().persist(o);
            }
        } else
            em.get().persist(entity);
    }

    @DMLPOJOAnnotation(action = "Update")
    public <E> E merge(E entity) {
        return em.get().merge(entity);
    }

    @DMLPOJOAnnotation(action = "Delete")
    public void remove(Object entity) {
        em.get().remove(entity);
    }

    @Override
    public T findById(Long id) {
        return this.find((Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0], id);
    }

    public T findById(Long id, LockModeType lockMode) {
        return this.find((Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0], id, lockMode);
    }
}
